<?php
/**
 * Created by PhpStorm.
 * User: yuanxr
 * Date: 2018/6/10
 * Time: 上午8:32
 */

namespace Utility\Data;
use \PDO;
use \Exception;
/**********************************************************************************************************************
 * /tsql：使用pdo操作mysql（OLTP），主要用于业务存取
 **********************************************************************************************************************/
class Tsql
{
    /**
     * query
     *
     * @param array $db 数据库数组
     * @param string||array $sqls 单条sql语句，或sql语句数组
     * @param array $params 查询mysql时与sql语句配合的参数
     * @return array 读取到数据，则返回数据数组。否则返回null
     */
    public function query(array $db, $sqls, $params = null)
    {
        try{
            $pdo =$this->_pdo($db);
            // 如果sqls是字符串，则将sqls和params都转换为数组，否则，验证其数量是否相等
            if (is_string($sqls)) {
                $sqls = [$sqls];
                $params = [$params];
            }
        
            // 执行数据查询，关闭pdo
            $stmts = [];
            $i = 0;
            foreach ($sqls as $key=>$sql) {
                $stmt = $pdo->prepare($sql);
                if (!$stmt->execute($params[$i])) {
                    throw new Exception('$this->query，查询时出现错误，出错语句：' . $sql . '，出错参数：' . $params[$i]);
                }
                $stmt->setFetchMode(PDO::FETCH_ASSOC);
                $stmts[] = $stmt;
                $i++;
            }
    
            // 将结果与MongoDB一样做成数组返回
            $result = null;
            if (count($stmts) === 1) {
                $result = $stmts[0]->fetchAll();
            } else {
                $result = [];
                $i = 0;
                foreach ($sqls as $key=>$val) {
                    $result[$key] = $stmts[$i]->fetchAll();
                    $i++;
                }
            }
            return $result;    
        }catch(Exception $e){
            echo $e->getMessage();exit();
        }
    }

    /**
     * write
     *
     * @param array $db 数据库数组
     * @param string||array $sqls 单条sql语句，或sql语句数组
     * @param array $params 查询mysql时与sql语句配合的参数
     * @return boolean 成功返回true，失败返回false
     */
    public function write(array $db, $sqls, $params = null)
    {
        try{
            // 如果是多条操作，则使用事物
            if (is_array($sqls)) {
                return$this->_transaction($db, $sqls, $params);
            }
            // 执行单条数据操作
            $pdo =$this->_pdo($db);
            $stmt = $pdo->prepare($sqls);
            $result = $stmt->execute($params);
            $pdo = null;
            if (!$result) {
                throw new Exception('$this->write，查询时出现错误，出错语句：' . $sqls);
            }
            return $result;
        }catch(Exception $e){
            echo $e->getMessage();exit();
        }
    }

    /**
     * lastid
     *
     * @param array $db 数据库数组
     * @param string $sql 单条sql语句
     * @param array $params 与sql配合的params参数的值
     * @return int 成功则返回被插入数据的id号
     */
    public function lastid(array $db, string $sql, $params = null)
    {
        try{
            $pdo =$this->_pdo($db);
            $stmt = $pdo->prepare($sql);
            $result = $stmt->execute($params);
            $lastId = $pdo->lastInsertId();
            $pdo = null;
            return $lastId;    
        }catch(Exception $e){
            echo $e->getMessage();exit();
        }
    }
    
    /**
     * _transaction
     */
    private function _transaction(array $db, array $sqls, array $params)
    {
        try{
            $pdo =$this->_pdo($db, 0);
            $pdo->beginTransaction();
            foreach ($sqls as $key=>$sql) {
                $stmt = $pdo->prepare($sql);
                if (!$stmt->execute($params[$key])) {
                    $pdo->rollBack();
                    throw new Exception('$this->_transaction，事物操作时出现错误，出错语句：' . $sql . '，出错参数：' . $params[$key]);
                    return false;
                }
            }
            $pdo->commit();
            $pdo = null;
            return true;
        }catch(Exception $e){
            echo $e->getMessage();exit();
        }
    }
    
    /**
     * _pdo
     */
    private function _pdo(array $db, int $autocommit = 1)
    {
        $pdo = new PDO($db['host'], $db['user'], $db['pwd']);
        $pdo->query("set names utf8");
        $pdo->setAttribute(PDO::ATTR_AUTOCOMMIT, $autocommit);
        $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        $pdo->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false);
        $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
        return $pdo;
    }

    /**
     * writeSyn
     * PDO的可回滚可同步的wirite
     */
    public function writeSyn(array $db, $sqls, $params = null, $function_sync=null, $data=null){
        $pdo = $this->_pdo($db);
        $pdo->beginTransaction();
        try{
            if(is_array($sqls)){
                foreach ($sqls as $key=>$sql){
                    $stmt = $pdo->prepare($sql);
                    if (!$stmt->execute($params[$key])) {
                        $pdo->rollBack();
                        throw new Exception('writeSyn，事物操作时出现错误，出错语句：' . $sql . '，出错参数：' . $params[$key]);
                        return false;
                    }
                }
            }else{
                $stmt = $pdo->prepare($sqls);
                $result = $stmt->execute($params);
            }
            $pdo->commit();
            $pdo = null;
            if($function_sync){
                if(!$function_sync($data)){
                    throw new Exception('writeSyn，注入方法执行错误');
                }
            }
        }catch(Exception $e){
            $pdo->rollBack();
            echo $e->getMessage();exit();
        }
    }


    /**
     * lastid 执行sql 同步或者做其他操作 ,最后取出最后一个id
     *
     */
    public function lastidSyn(array $db, string $sql, $params = null, $function_sync=null, $data=null){
        $pdo = $this->_pdo($db);
        $pdo->beginTransaction();
        try{
            $stmt = $pdo->prepare($sql);
            $result = $stmt->execute($params);
            $lastId = $pdo->lastInsertId();
            $pdo->commit();
            $pdo = null;
            if(!$function_sync){
                throw new Exception('lastidSyn，注入方法执行错误');
            }
        }catch(Exception $e){
            $pdo->rollBack();
            echo $e->getMessage();exit();
        }
        return $lastId;
    }


}